001 /* 002 * Copyright 2005 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.tools.tasks; 020 021 import java.io.File; 022 import java.io.OutputStream; 023 import java.io.FileOutputStream; 024 import java.io.IOException; 025 import java.net.URI; 026 import java.util.ArrayList; 027 import java.util.List; 028 029 import net.dpml.lang.Classpath; 030 import net.dpml.lang.Category; 031 import net.dpml.lang.Info; 032 import net.dpml.lang.Part; 033 import net.dpml.lang.PartDecoder; 034 035 import net.dpml.library.Type; 036 import net.dpml.library.Resource; 037 038 import net.dpml.transit.Artifact; 039 040 import net.dpml.tools.Context; 041 042 import net.dpml.util.DefaultLogger; 043 044 import org.apache.tools.ant.Project; 045 import org.apache.tools.ant.BuildException; 046 047 import org.w3c.dom.Element; 048 049 /** 050 * Creation of an part definition in XML. 051 * 052 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 053 * @version 1.1.0 054 */ 055 public class PartTask extends GenericTask 056 { 057 /** 058 * Constant artifact type for a plugin. 059 */ 060 public static final String TYPE = "part"; 061 062 /** 063 * Property key used to identify the plugin title. 064 */ 065 public static final String PLUGIN_TITLE_KEY = "project.title"; 066 067 /** 068 * Property key used to identify the plugin description. 069 */ 070 public static final String PLUGIN_DESCRIPTION_KEY = "project.description"; 071 072 /** 073 * Property key used to identify a custom plugin handler classname. 074 */ 075 public static final String PLUGIN_HANDLER_KEY = "project.plugin.handler"; 076 077 private boolean m_test = false; 078 079 private File m_output; 080 081 /** 082 * Set the test build policy. The default is to include 083 * the project artifact in the classpath of a created part, however - in a 084 * test scenario we don't want to do this. Setting test to true will result 085 * in the association of a local file uri to the project resource. 086 * 087 * @param test true if this is a local test part 088 */ 089 public void setTest( boolean test ) 090 { 091 m_test = test; 092 } 093 094 /** 095 * Override the default output destination. 096 * 097 * @param file the overriding destination 098 */ 099 public void setDest( File file ) 100 { 101 m_output = file; 102 } 103 104 /** 105 * Task execution. 106 */ 107 public void execute() 108 { 109 Resource resource = getResource(); 110 Part part = build( resource ); 111 writePart( part ); 112 } 113 114 /** 115 * Externalize the part as a deliverable. 116 * @param part the part to be externalized as XML 117 */ 118 public void writePart( Part part ) 119 { 120 File file = getOutputFile(); 121 if( file.exists() ) 122 { 123 try 124 { 125 Part existing = Part.load( file.toURI(), false ); 126 if( part.equals( existing ) ) 127 { 128 return; 129 } 130 } 131 catch( Exception e ) 132 { 133 // continue 134 } 135 } 136 137 log( "Building part: " + file ); 138 try 139 { 140 file.createNewFile(); 141 final OutputStream output = new FileOutputStream( file ); 142 try 143 { 144 part.encode( output ); 145 checksum( file ); 146 asc( file ); 147 } 148 finally 149 { 150 try 151 { 152 output.close(); 153 } 154 catch( IOException e ) 155 { 156 e.printStackTrace(); 157 } 158 } 159 } 160 catch( Exception e ) 161 { 162 final String error = 163 "Part externalization error."; 164 throw new BuildException( error, e ); 165 } 166 } 167 168 private File getOutputFile() 169 { 170 if( null != m_output ) 171 { 172 return m_output; 173 } 174 else 175 { 176 Context context = getContext(); 177 final String path = context.getLayoutFilename( TYPE ); 178 final File deliverables = context.getTargetDeliverablesDirectory(); 179 final File parts = new File( deliverables, TYPE + "s" ); 180 final File file = new File( parts, path ); 181 parts.mkdirs(); 182 return file; 183 } 184 } 185 186 /** 187 * Build the part definition. 188 * @param resource the resource 189 * @return the part 190 */ 191 protected Part build( Resource resource ) 192 { 193 try 194 { 195 Info info = getInfo( resource ); 196 Classpath classpath = getClasspath( resource ); 197 Type type = resource.getType( TYPE ); 198 Element element = type.getElement(); 199 PartDecoder decoder = PartDecoder.getInstance(); 200 DefaultLogger logger = new DefaultLogger( getTaskName() ); 201 return decoder.build( logger, info, classpath, element, resource ); 202 } 203 catch( Throwable e ) 204 { 205 final String error = 206 "Internal error while attempting to build an external part definition." 207 + "\nResource: " + resource; 208 throw new BuildException( error, e, getLocation() ); 209 } 210 } 211 212 /** 213 * Construct the info object based on properties declared by the 214 * supplied resource. 215 * @param resource the resource 216 * @return the info descriptor 217 */ 218 protected Info getInfo( Resource resource ) 219 { 220 Artifact artifact = resource.getArtifact( TYPE ); 221 URI uri = artifact.toURI(); 222 String title = resource.getInfo().getTitle(); 223 String description = resource.getInfo().getDescription(); 224 return new Info( uri, title, description ); 225 } 226 227 private String getTitle( Resource resource ) 228 { 229 return resource.getProperty( PLUGIN_TITLE_KEY ); 230 } 231 232 private String getDescription( Resource resource ) 233 { 234 return resource.getProperty( PLUGIN_DESCRIPTION_KEY ); 235 } 236 237 /** 238 * Construct the classpath for the supplied resource. 239 * @param resource the resource 240 * @return the classpath 241 * @exception IOException is an IO error occurs 242 */ 243 protected Classpath getClasspath( Resource resource ) throws IOException 244 { 245 URI[] sysUris = getURIs( resource, Category.SYSTEM ); 246 URI[] publicUris = getURIs( resource, Category.PUBLIC ); 247 URI[] protectedUris = getURIs( resource, Category.PROTECTED ); 248 URI[] privateUris = getURIs( resource, Category.PRIVATE, true ); 249 return new Classpath( sysUris, publicUris, protectedUris, privateUris ); 250 } 251 252 private URI[] getURIs( Resource resource, Category category ) throws IOException 253 { 254 return getURIs( resource, category, false ); 255 } 256 257 private URI[] getURIs( Resource resource, Category category, boolean self ) throws IOException 258 { 259 Resource[] resources = resource.getClasspathProviders( category ); 260 ArrayList list = new ArrayList(); 261 for( int i=0; i<resources.length; i++ ) 262 { 263 Resource r = resources[i]; 264 addURI( list, r ); 265 } 266 if( self ) 267 { 268 addURI( list, resource ); 269 } 270 URI[] uris = (URI[]) list.toArray( new URI[0] ); 271 return uris; 272 } 273 274 private void addURI( List list, Resource resource ) throws IOException 275 { 276 if( resource.isa( "jar" ) ) 277 { 278 try 279 { 280 URI uri = toURI( resource ); 281 list.add( uri ); 282 } 283 catch( Exception e ) 284 { 285 final String error = 286 "Unexpected error while attempting to resolve resource."; 287 IOException ioe = new IOException( error ); 288 ioe.initCause( e ); 289 throw ioe; 290 } 291 } 292 } 293 294 private URI toURI( Resource resource ) throws Exception 295 { 296 if( m_test && resource.equals( getResource() ) ) 297 { 298 File local = getContext().getTargetDeliverable( "jar" ); 299 return local.toURI(); 300 } 301 else 302 { 303 return resource.getArtifact( "jar" ).toURI(); 304 } 305 } 306 307 /** 308 * Get the project definition. 309 * @param project the project 310 * @return the build context 311 */ 312 protected Context getContext( Project project ) 313 { 314 Context context = (Context) project.getReference( "project.context" ); 315 context.init(); 316 return context; 317 } 318 }